package easyurl

import (
	"bytes"
	"crypto/tls"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"strings"
	"time"

	"gitee.com/haodreams/libs/ee"
	"gitee.com/haodreams/libs/proxy"
	"golang.org/x/text/encoding/simplifiedchinese"
)

var defaultTimeout = int64(60000)

type BasicAuth struct {
	UserName string
	Password string
}

type Options struct {
	*BasicAuth
	Header  [][]string
	Body    []byte
	Method  string
	Proxy   string //代理
	Timeout int64  //ms
	Decode  string //数据是否需要解码
}

func NewDefaultOptions() *Options {
	return &Options{
		Method:  "GET",
		Timeout: defaultTimeout,
	}
}

func WithProxy(proxy string) Option {
	return func(c *Options) {
		c.Proxy = proxy
	}
}

func WithBaicAuth(user, password string) Option {
	return func(c *Options) {
		c.BasicAuth = &BasicAuth{
			UserName: user,
			Password: password,
		}
	}
}

/**
 * @description: 增加GBK解码
 * @return {*}
 */
func WithDecodeGBK() Option {
	return func(c *Options) {
		c.Decode = "GBK"
	}
}

// AddHeader 增加header
func (m *Options) AddHeader(key, value string) {
	m.Header = append(m.Header, []string{key, value})
}

// WithBody
func WithBody(body []byte) Option {
	return func(c *Options) {
		c.Body = body
	}
}

func WithJSON(v any) Option {
	return func(c *Options) {
		c.Body, _ = json.Marshal(v)
	}
}

func WithHeader(key, value string) Option {
	return func(c *Options) {
		c.AddHeader(key, value)
	}
}

// WithTimeout ms
func WithTimeout(timeout int64) Option {
	return func(c *Options) {
		c.Timeout = timeout
	}
}
func WithPost() Option {
	return func(c *Options) {
		c.Method = "POST"
	}
}

// 选项函数
type Option func(*Options)

/**
 * @description: 自定义选项
 * @return {*}
 */
func NewOptions(options ...Option) *Options {
	os := new(Options)
	for _, f := range options {
		f(os)
	}
	if os.Method == "" {
		os.Method = "GET"
	}
	if os.Timeout == 0 {
		os.Timeout = defaultTimeout
	}
	return os
}

func Get(uri string, options *Options) (data []byte, code int, err error) {
	if options == nil {
		options = NewDefaultOptions()
	}
	options.Method = "GET"
	return Request(uri, options)
}

func Post(uri string, options *Options) (data []byte, code int, err error) {
	if options == nil {
		options = NewDefaultOptions()
	}
	options.Method = "POST"
	return Request(uri, options)
}

type Opt func() Option

func GET(uri string, opts ...Option) (data []byte, code int, err error) {
	options := NewDefaultOptions()
	for _, opt := range opts {
		opt(options)
	}
	options.Method = "GET"
	return Request(uri, options)
}

func POST(uri string, opts ...Option) (data []byte, code int, err error) {
	options := NewDefaultOptions()
	for _, opt := range opts {
		opt(options)
	}
	options.Method = "POST"
	return Request(uri, options)
}

func PostForm(uri string, values url.Values, options *Options) (data []byte, code int, err error) {
	if options == nil {
		options = NewDefaultOptions()
	}
	options.Method = "POST"
	options.AddHeader("Content-Type", "application/x-www-form-urlencoded")
	options.Body = []byte(values.Encode())
	return Request(uri, options)
}

func JSON(uri string, Any any, options ...Option) (data []byte, code int, err error) {
	opts := NewDefaultOptions()
	opts.Method = "POST"
	opts.Timeout = 60000
	for _, opt := range options {
		opt(opts)
	}
	opts.AddHeader("Content-Type", "application/json; charset=utf-8")
	if s, ok := Any.(string); ok {
		opts.Body = []byte(s)
	} else if s, ok := Any.([]byte); ok {
		opts.Body = s
	} else {
		jsonData, err := json.Marshal(Any)
		if err != nil {
			return nil, -1, err
		}
		opts.Body = jsonData
	}
	return Request(uri, opts)
}

/**
 * @description: 默认使用GET方法
 * @param {string} uri
 * @param {*Options} options
 * @return {*}
 */
func Request(uri string, options *Options) (data []byte, code int, err error) {
	client := http.Client{}
	if options == nil {
		options = NewDefaultOptions()
	}
	client.Timeout = time.Duration(options.Timeout) * time.Millisecond

	if strings.HasPrefix(uri, "https://") {
		tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
		client.Transport = tr
	}

	request, err := http.NewRequest(options.Method, uri, bytes.NewBuffer(options.Body))
	if err != nil {
		err = ee.NewError(fmt.Sprint(uri, err.Error()))
		return
	}

	//设置头部选项
	if options != nil && len(options.Header) > 0 {
		for _, header := range options.Header {
			request.Header.Add(header[0], header[1])
		}
	}

	if options.BasicAuth != nil {
		request.SetBasicAuth(options.UserName, options.Password)
	}

	if options.Proxy != "" {
		d, err := proxy.ContextDialFunc(options.Proxy)
		if err != nil {
			return nil, 0, ee.NewError(fmt.Sprint(uri, err.Error()))
		}
		if client.Transport == nil {
			client.Transport = &http.Transport{DialContext: d}
		} else {
			if tr, ok := client.Transport.(*http.Transport); ok && tr != nil {
				tr.DialContext = d
			}
		}
	}

	rsp, err := client.Do(request)
	if err != nil {
		err = ee.NewError(fmt.Sprint(uri, " : ", err.Error()))
		return
	}
	defer func() {
		rsp.Body.Close()
	}()

	code = rsp.StatusCode

	var body io.Reader
	body = rsp.Body
	switch options.Decode {
	case "GBK":
		decoder := simplifiedchinese.GBK.NewDecoder()
		body = decoder.Reader(rsp.Body)
	}

	data, err = io.ReadAll(body)
	// 强制关闭所有空闲连接
	if transport, ok := client.Transport.(*http.Transport); ok {
		transport.CloseIdleConnections()
	}

	if err != nil {
		err = ee.NewError(fmt.Sprint(uri, err.Error()))
		return
	}

	return
}

func Base64Encode(data string) string {
	return base64.StdEncoding.EncodeToString([]byte(data))
}
